Skip to main content

mofa_plugins/tools/
datetime.rs

1use super::*;
2use chrono::{DateTime, Local, TimeZone, Utc};
3use serde_json::json;
4
5/// 日期时间工具 - 获取当前时间、格式化日期
6pub struct DateTimeTool {
7    definition: ToolDefinition,
8}
9
10impl Default for DateTimeTool {
11    fn default() -> Self {
12        Self::new()
13    }
14}
15
16impl DateTimeTool {
17    pub fn new() -> Self {
18        Self {
19            definition: ToolDefinition {
20                name: "datetime".to_string(),
21                description: "Date and time operations: get current time, format dates, calculate time differences.".to_string(),
22                parameters: json!({
23                    "type": "object",
24                    "properties": {
25                        "operation": {
26                            "type": "string",
27                            "enum": ["now", "format", "parse", "add", "diff"],
28                            "description": "Operation to perform"
29                        },
30                        "format": {
31                            "type": "string",
32                            "description": "Date format string (e.g., '%Y-%m-%d %H:%M:%S')"
33                        },
34                        "timestamp": {
35                            "type": "integer",
36                            "description": "Unix timestamp in seconds"
37                        },
38                        "date_string": {
39                            "type": "string",
40                            "description": "Date string to parse"
41                        },
42                        "timezone": {
43                            "type": "string",
44                            "description": "Timezone (e.g., 'UTC', 'Local')"
45                        },
46                        "duration_seconds": {
47                            "type": "integer",
48                            "description": "Duration in seconds for add operation"
49                        }
50                    },
51                    "required": ["operation"]
52                }),
53                requires_confirmation: false,
54            },
55        }
56    }
57}
58
59#[async_trait::async_trait]
60impl ToolExecutor for DateTimeTool {
61    fn definition(&self) -> &ToolDefinition {
62        &self.definition
63    }
64
65    async fn execute(&self, arguments: serde_json::Value) -> PluginResult<serde_json::Value> {
66        let operation = arguments["operation"]
67            .as_str()
68            .ok_or_else(|| anyhow::anyhow!("Operation is required"))?;
69
70        match operation {
71            "now" => {
72                let now_utc = Utc::now();
73                let now_local = Local::now();
74                Ok(json!({
75                    "utc": now_utc.to_rfc3339(),
76                    "local": now_local.to_rfc3339(),
77                    "timestamp": now_utc.timestamp(),
78                    "timestamp_millis": now_utc.timestamp_millis(),
79                    "formatted": now_local.format("%Y-%m-%d %H:%M:%S").to_string()
80                }))
81            }
82            "format" => {
83                let timestamp = arguments["timestamp"]
84                    .as_i64()
85                    .ok_or_else(|| anyhow::anyhow!("Timestamp is required for format operation"))?;
86                let format = arguments["format"].as_str().unwrap_or("%Y-%m-%d %H:%M:%S");
87                let timezone = arguments["timezone"].as_str().unwrap_or("UTC");
88
89                let formatted = if timezone == "Local" {
90                    Local
91                        .timestamp_opt(timestamp, 0)
92                        .single()
93                        .map(|dt| dt.format(format).to_string())
94                } else {
95                    Utc.timestamp_opt(timestamp, 0)
96                        .single()
97                        .map(|dt| dt.format(format).to_string())
98                };
99
100                match formatted {
101                    Some(f) => Ok(json!({
102                        "formatted": f,
103                        "timestamp": timestamp
104                    })),
105                    None => Err(anyhow::anyhow!("Invalid timestamp")),
106                }
107            }
108            "parse" => {
109                let date_string = arguments["date_string"].as_str().ok_or_else(|| {
110                    anyhow::anyhow!("date_string is required for parse operation")
111                })?;
112
113                // Try RFC3339 first
114                if let Ok(dt) = DateTime::parse_from_rfc3339(date_string) {
115                    return Ok(json!({
116                        "timestamp": dt.timestamp(),
117                        "utc": dt.to_utc().to_rfc3339()
118                    }));
119                }
120
121                // Try common format
122                if let Ok(dt) =
123                    chrono::NaiveDateTime::parse_from_str(date_string, "%Y-%m-%d %H:%M:%S")
124                {
125                    return Ok(json!({
126                        "timestamp": dt.and_utc().timestamp(),
127                        "utc": dt.and_utc().to_rfc3339()
128                    }));
129                }
130
131                Err(anyhow::anyhow!(
132                    "Could not parse date string: {}",
133                    date_string
134                ))
135            }
136            "add" => {
137                let timestamp = arguments["timestamp"]
138                    .as_i64()
139                    .unwrap_or_else(|| Utc::now().timestamp());
140                let duration = arguments["duration_seconds"].as_i64().ok_or_else(|| {
141                    anyhow::anyhow!("duration_seconds is required for add operation")
142                })?;
143
144                let new_timestamp = timestamp + duration;
145                let dt = Utc.timestamp_opt(new_timestamp, 0).single();
146
147                match dt {
148                    Some(dt) => Ok(json!({
149                        "original_timestamp": timestamp,
150                        "new_timestamp": new_timestamp,
151                        "utc": dt.to_rfc3339()
152                    })),
153                    None => Err(anyhow::anyhow!("Invalid resulting timestamp")),
154                }
155            }
156            "diff" => {
157                let ts1 = arguments["timestamp1"]
158                    .as_i64()
159                    .ok_or_else(|| anyhow::anyhow!("timestamp1 is required for diff operation"))?;
160                let ts2 = arguments["timestamp2"]
161                    .as_i64()
162                    .ok_or_else(|| anyhow::anyhow!("timestamp2 is required for diff operation"))?;
163
164                let diff = ts2 - ts1;
165                let days = diff / 86400;
166                let hours = (diff % 86400) / 3600;
167                let minutes = (diff % 3600) / 60;
168                let seconds = diff % 60;
169
170                Ok(json!({
171                    "diff_seconds": diff,
172                    "diff_human": format!("{}d {}h {}m {}s", days, hours, minutes, seconds)
173                }))
174            }
175            _ => Err(anyhow::anyhow!("Unknown operation: {}", operation)),
176        }
177    }
178}