mofa_plugins/tools/
datetime.rs1use super::*;
2use chrono::{DateTime, Local, TimeZone, Utc};
3use serde_json::json;
4
5pub 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 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 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}