1use async_trait::async_trait;
7use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveDateTime, TimeZone, Weekday};
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10
11use lc_core::tools::{BaseTool, Tool, ToolError};
12
13#[derive(Debug, Deserialize, JsonSchema)]
15pub struct DateTimeInput {
16 pub operation: String,
18
19 pub datetime: Option<String>,
21
22 pub unit: Option<String>,
24
25 pub value: Option<i64>,
27
28 pub target: Option<String>,
30}
31
32#[derive(Debug, Serialize)]
34pub struct DateTimeOutput {
35 pub result: String,
37
38 pub operation: String,
40
41 pub details: Option<String>,
43}
44
45pub struct DateTimeTool;
47
48impl DateTimeTool {
49 pub fn new() -> Self {
51 Self
52 }
53
54 fn parse_datetime(&self, dt_str: &str) -> Result<DateTime<Local>, ToolError> {
55 if let Ok(dt) = NaiveDateTime::parse_from_str(dt_str, "%Y-%m-%d %H:%M:%S") {
56 return Local
57 .from_local_datetime(&dt)
58 .single()
59 .ok_or_else(|| ToolError::ExecutionFailed("无效的日期时间".to_string()));
60 }
61
62 if let Ok(date) = NaiveDate::parse_from_str(dt_str, "%Y-%m-%d") {
63 let dt = date
64 .and_hms_opt(0, 0, 0)
65 .ok_or_else(|| ToolError::ExecutionFailed("无效的日期".to_string()))?;
66 return Local
67 .from_local_datetime(&dt)
68 .single()
69 .ok_or_else(|| ToolError::ExecutionFailed("无效的日期时间".to_string()));
70 }
71
72 Err(ToolError::ExecutionFailed(format!(
73 "无法解析日期时间: {},请使用格式 YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS",
74 dt_str
75 )))
76 }
77
78 fn get_now(&self) -> DateTimeOutput {
79 let now = Local::now();
80 DateTimeOutput {
81 result: now.format("%Y-%m-%d %H:%M:%S").to_string(),
82 operation: "now".to_string(),
83 details: Some(format!(
84 "星期{},第{}周",
85 match now.weekday() {
86 Weekday::Mon => "一",
87 Weekday::Tue => "二",
88 Weekday::Wed => "三",
89 Weekday::Thu => "四",
90 Weekday::Fri => "五",
91 Weekday::Sat => "六",
92 Weekday::Sun => "日",
93 },
94 now.iso_week().week()
95 )),
96 }
97 }
98
99 fn format_datetime(&self, dt_str: &str) -> Result<DateTimeOutput, ToolError> {
100 let dt = self.parse_datetime(dt_str)?;
101
102 Ok(DateTimeOutput {
103 result: dt.format("%Y年%m月%d日 %H时%M分%S秒").to_string(),
104 operation: "format".to_string(),
105 details: Some(format!(
106 "星期{},{}年{}周",
107 match dt.weekday() {
108 Weekday::Mon => "一",
109 Weekday::Tue => "二",
110 Weekday::Wed => "三",
111 Weekday::Thu => "四",
112 Weekday::Fri => "五",
113 Weekday::Sat => "六",
114 Weekday::Sun => "日",
115 },
116 dt.year(),
117 dt.iso_week().week()
118 )),
119 })
120 }
121
122 fn add_time(&self, dt_str: &str, value: i64, unit: &str) -> Result<DateTimeOutput, ToolError> {
123 let dt = self.parse_datetime(dt_str)?;
124
125 let new_dt = match unit {
126 "seconds" => dt + Duration::seconds(value),
127 "minutes" => dt + Duration::minutes(value),
128 "hours" => dt + Duration::hours(value),
129 "days" => dt + Duration::days(value),
130 "weeks" => dt + Duration::weeks(value),
131 "months" => {
132 let months = value as i32;
133 let new_month = dt.month() as i32 + months;
134 let year = dt.year() + (new_month - 1) / 12;
135 let month = ((new_month - 1) % 12 + 1) as u32;
136
137 dt.with_year(year)
138 .and_then(|d| d.with_month(month))
139 .ok_or_else(|| ToolError::ExecutionFailed("月份计算失败".to_string()))?
140 }
141 "years" => dt
142 .with_year(dt.year() + value as i32)
143 .ok_or_else(|| ToolError::ExecutionFailed("年份计算失败".to_string()))?,
144 _ => {
145 return Err(ToolError::ExecutionFailed(format!(
146 "不支持的时间单位: {},请使用: seconds, minutes, hours, days, weeks, months, years",
147 unit
148 )))
149 }
150 };
151
152 Ok(DateTimeOutput {
153 result: new_dt.format("%Y-%m-%d %H:%M:%S").to_string(),
154 operation: "add".to_string(),
155 details: Some(format!("{} {} 后", value, self.unit_to_chinese(unit))),
156 })
157 }
158
159 fn subtract_time(
160 &self,
161 dt_str: &str,
162 value: i64,
163 unit: &str,
164 ) -> Result<DateTimeOutput, ToolError> {
165 self.add_time(dt_str, -value, unit)
166 }
167
168 fn get_weekday(&self, dt_str: &str) -> Result<DateTimeOutput, ToolError> {
169 let dt = self.parse_datetime(dt_str)?;
170
171 let weekday = match dt.weekday() {
172 Weekday::Mon => "星期一",
173 Weekday::Tue => "星期二",
174 Weekday::Wed => "星期三",
175 Weekday::Thu => "星期四",
176 Weekday::Fri => "星期五",
177 Weekday::Sat => "星期六",
178 Weekday::Sun => "星期日",
179 };
180
181 Ok(DateTimeOutput {
182 result: weekday.to_string(),
183 operation: "weekday".to_string(),
184 details: Some(format!("{} 是 {}", dt.format("%Y-%m-%d"), weekday)),
185 })
186 }
187
188 fn diff_time(&self, dt1_str: &str, dt2_str: &str) -> Result<DateTimeOutput, ToolError> {
189 let dt1 = self.parse_datetime(dt1_str)?;
190 let dt2 = self.parse_datetime(dt2_str)?;
191
192 let diff = dt2.signed_duration_since(dt1);
193
194 let days = diff.num_days();
195 let hours = diff.num_hours() % 24;
196 let minutes = diff.num_minutes() % 60;
197
198 Ok(DateTimeOutput {
199 result: format!("{}天 {}小时 {}分钟", days.abs(), hours.abs(), minutes.abs()),
200 operation: "diff".to_string(),
201 details: Some(if diff.num_seconds() >= 0 {
202 format!(
203 "从 {} 到 {} 相隔 {}天 {}小时 {}分钟",
204 dt1.format("%Y-%m-%d"),
205 dt2.format("%Y-%m-%d"),
206 days,
207 hours,
208 minutes
209 )
210 } else {
211 format!(
212 "从 {} 到 {} 相隔 {}天 {}小时 {}分钟",
213 dt2.format("%Y-%m-%d"),
214 dt1.format("%Y-%m-%d"),
215 days.abs(),
216 hours.abs(),
217 minutes.abs()
218 )
219 }),
220 })
221 }
222
223 fn unit_to_chinese(&self, unit: &str) -> String {
224 match unit {
225 "seconds" => "秒",
226 "minutes" => "分钟",
227 "hours" => "小时",
228 "days" => "天",
229 "weeks" => "周",
230 "months" => "月",
231 "years" => "年",
232 _ => unit,
233 }
234 .to_string()
235 }
236}
237
238impl Default for DateTimeTool {
239 fn default() -> Self {
240 Self::new()
241 }
242}
243
244#[async_trait]
245impl Tool for DateTimeTool {
246 type Input = DateTimeInput;
247 type Output = DateTimeOutput;
248
249 async fn invoke(&self, input: Self::Input) -> Result<Self::Output, ToolError> {
250 match input.operation.as_str() {
251 "now" => Ok(self.get_now()),
252 "format" => {
253 let dt = input.datetime.ok_or_else(|| {
254 ToolError::InvalidInput("format 操作需要 datetime 参数".to_string())
255 })?;
256 self.format_datetime(&dt)
257 }
258 "add" => {
259 let dt = input.datetime.ok_or_else(|| {
260 ToolError::InvalidInput("add 操作需要 datetime 参数".to_string())
261 })?;
262 let value = input.value.ok_or_else(|| {
263 ToolError::InvalidInput("add 操作需要 value 参数".to_string())
264 })?;
265 let unit = input
266 .unit
267 .ok_or_else(|| ToolError::InvalidInput("add 操作需要 unit 参数".to_string()))?;
268 self.add_time(&dt, value, &unit)
269 }
270 "subtract" => {
271 let dt = input.datetime.ok_or_else(|| {
272 ToolError::InvalidInput("subtract 操作需要 datetime 参数".to_string())
273 })?;
274 let value = input.value.ok_or_else(|| {
275 ToolError::InvalidInput("subtract 操作需要 value 参数".to_string())
276 })?;
277 let unit = input.unit.ok_or_else(|| {
278 ToolError::InvalidInput("subtract 操作需要 unit 参数".to_string())
279 })?;
280 self.subtract_time(&dt, value, &unit)
281 }
282 "weekday" => {
283 let dt = input.datetime.ok_or_else(|| {
284 ToolError::InvalidInput("weekday 操作需要 datetime 参数".to_string())
285 })?;
286 self.get_weekday(&dt)
287 }
288 "diff" => {
289 let dt1 = input.datetime.ok_or_else(|| {
290 ToolError::InvalidInput("diff 操作需要 datetime 参数".to_string())
291 })?;
292 let dt2 = input.target.ok_or_else(|| {
293 ToolError::InvalidInput("diff 操作需要 target 参数".to_string())
294 })?;
295 self.diff_time(&dt1, &dt2)
296 }
297 _ => Err(ToolError::InvalidInput(format!(
298 "不支持的操作: {},请使用: now, format, add, subtract, weekday, diff",
299 input.operation
300 ))),
301 }
302 }
303}
304
305#[async_trait]
306impl BaseTool for DateTimeTool {
307 fn name(&self) -> &str {
308 "datetime"
309 }
310
311 fn description(&self) -> &str {
312 "日期时间工具。支持多种操作:
313
314操作类型:
315- now: 获取当前时间
316- format: 格式化日期时间
317- add: 添加时间
318- subtract: 减去时间
319- weekday: 获取星期几
320- diff: 计算时间差
321
322示例:
323- 获取当前时间: {\"operation\": \"now\"}
324- 格式化日期: {\"operation\": \"format\", \"datetime\": \"2024-01-15\"}
325- 添加3天: {\"operation\": \"add\", \"datetime\": \"2024-01-15\", \"value\": 3, \"unit\": \"days\"}
326- 计算差值: {\"operation\": \"diff\", \"datetime\": \"2024-01-01\", \"target\": \"2024-01-15\"}"
327 }
328
329 async fn run(&self, input: String) -> Result<String, ToolError> {
330 let parsed: DateTimeInput = serde_json::from_str(&input)
331 .map_err(|e| ToolError::InvalidInput(format!("JSON 解析失败: {}", e)))?;
332
333 let output = self.invoke(parsed).await?;
334
335 Ok(format!(
336 "{}\n详细信息: {}",
337 output.result,
338 output.details.unwrap_or_default()
339 ))
340 }
341
342 fn args_schema(&self) -> Option<serde_json::Value> {
343 use schemars::schema_for;
344 serde_json::to_value(schema_for!(DateTimeInput)).ok()
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[tokio::test]
353 async fn test_datetime_now() {
354 let tool = DateTimeTool::new();
355
356 let input = DateTimeInput {
357 operation: "now".to_string(),
358 datetime: None,
359 unit: None,
360 value: None,
361 target: None,
362 };
363
364 let result = tool.invoke(input).await.unwrap();
365 assert!(!result.result.is_empty());
366 assert!(result.details.is_some());
367 }
368
369 #[tokio::test]
370 async fn test_datetime_format() {
371 let tool = DateTimeTool::new();
372
373 let input = DateTimeInput {
374 operation: "format".to_string(),
375 datetime: Some("2024-01-15".to_string()),
376 unit: None,
377 value: None,
378 target: None,
379 };
380
381 let result = tool.invoke(input).await.unwrap();
382 assert!(result.result.contains("2024年"));
383 assert!(result.result.contains("01月"));
384 assert!(result.result.contains("15日"));
385 }
386
387 #[tokio::test]
388 async fn test_datetime_add_days() {
389 let tool = DateTimeTool::new();
390
391 let input = DateTimeInput {
392 operation: "add".to_string(),
393 datetime: Some("2024-01-15".to_string()),
394 unit: Some("days".to_string()),
395 value: Some(3),
396 target: None,
397 };
398
399 let result = tool.invoke(input).await.unwrap();
400 assert!(result.result.contains("2024-01-18"));
401 }
402
403 #[tokio::test]
404 async fn test_datetime_weekday() {
405 let tool = DateTimeTool::new();
406
407 let input = DateTimeInput {
409 operation: "weekday".to_string(),
410 datetime: Some("2024-01-15".to_string()),
411 unit: None,
412 value: None,
413 target: None,
414 };
415
416 let result = tool.invoke(input).await.unwrap();
417 assert_eq!(result.result, "星期一");
418 }
419
420 #[tokio::test]
421 async fn test_datetime_diff() {
422 let tool = DateTimeTool::new();
423
424 let input = DateTimeInput {
425 operation: "diff".to_string(),
426 datetime: Some("2024-01-01".to_string()),
427 unit: None,
428 value: None,
429 target: Some("2024-01-15".to_string()),
430 };
431
432 let result = tool.invoke(input).await.unwrap();
433 assert!(result.result.contains("14天"));
434 }
435}