File size: 6,290 Bytes
eca5751
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""DateTime Tool - time/date operations."""
from __future__ import annotations

from datetime import datetime, timezone, timedelta
from typing import Dict, Any

from .base import Tool, ToolResult, ToolContext, ToolCategory, ToolSafety


class DateTimeTool(Tool):
    """DateTime operations: now, parse, format, convert timezone, arithmetic."""
    category = ToolCategory.SYSTEM
    safety = ToolSafety.SAFE
    
    @property
    def name(self) -> str:
        return "datetime"
    
    @property
    def description(self) -> str:
        return (
            "DateTime operations: now, parse, format, timezone convert, "
            "date arithmetic, weekday, days between."
        )
    
    @property
    def parameters(self) -> Dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "enum": ["now", "parse", "format", "convert_tz", "add", "diff"],
                    "default": "now",
                },
                "datetime_str": {"type": "string", "description": "For parse/format/convert: input datetime"},
                "format_str": {"type": "string", "description": "strftime/strptime format"},
                "from_tz": {"type": "string", "description": "Source timezone (IANA name)"},
                "to_tz": {"type": "string", "description": "Target timezone"},
                "delta_days": {"type": "integer", "description": "Days to add (can be negative)"},
                "delta_hours": {"type": "integer"},
                "start": {"type": "string", "description": "For diff: start datetime"},
                "end": {"type": "string", "description": "For diff: end datetime"},
            },
        }
    
    def execute(self, args: Dict[str, Any], context: ToolContext) -> ToolResult:
        action = args.get("action", "now")
        
        try:
            if action == "now":
                now_utc = datetime.now(timezone.utc)
                now_local = datetime.now().astimezone()
                return ToolResult(
                    success=True,
                    output=(
                        f"UTC: {now_utc.isoformat()}\n"
                        f"Local: {now_local.isoformat()}\n"
                        f"Timestamp: {now_utc.timestamp()}"
                    ),
                    metadata={
                        "utc": now_utc.isoformat(),
                        "local": now_local.isoformat(),
                        "timestamp": now_utc.timestamp(),
                        "timezone": str(now_local.tzinfo),
                    },
                )
            
            elif action == "parse":
                dt_str = args["datetime_str"]
                fmt = args.get("format_str")
                if fmt:
                    dt = datetime.strptime(dt_str, fmt)
                else:
                    # Try ISO format
                    dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
                return ToolResult(
                    success=True,
                    output=f"Parsed: {dt.isoformat()}",
                    metadata={"parsed": dt.isoformat(), "weekday": dt.strftime("%A")},
                )
            
            elif action == "format":
                dt_str = args["datetime_str"]
                fmt = args.get("format_str", "%Y-%m-%d %H:%M:%S")
                dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
                return ToolResult(
                    success=True,
                    output=dt.strftime(fmt),
                    metadata={"format": fmt},
                )
            
            elif action == "convert_tz":
                dt_str = args["datetime_str"]
                from_tz = args.get("from_tz", "UTC")
                to_tz = args["to_tz"]
                
                try:
                    from zoneinfo import ZoneInfo
                    tz_from = ZoneInfo(from_tz)
                    tz_to = ZoneInfo(to_tz)
                except ImportError:
                    return ToolResult(
                        success=False,
                        error="zoneinfo not available (Python 3.9+)",
                        return_code=1,
                    )
                
                dt = datetime.fromisoformat(dt_str).replace(tzinfo=tz_from)
                converted = dt.astimezone(tz_to)
                return ToolResult(
                    success=True,
                    output=f"{dt_str} ({from_tz}) → {converted.isoformat()} ({to_tz})",
                    metadata={"original": dt.isoformat(), "converted": converted.isoformat()},
                )
            
            elif action == "add":
                dt_str = args["datetime_str"]
                delta_days = args.get("delta_days", 0)
                delta_hours = args.get("delta_hours", 0)
                dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
                new_dt = dt + timedelta(days=delta_days, hours=delta_hours)
                return ToolResult(
                    success=True,
                    output=f"{dt.isoformat()} + {delta_days}d {delta_hours}h = {new_dt.isoformat()}",
                    metadata={"result": new_dt.isoformat()},
                )
            
            elif action == "diff":
                start = datetime.fromisoformat(args["start"].replace("Z", "+00:00"))
                end = datetime.fromisoformat(args["end"].replace("Z", "+00:00"))
                delta = end - start
                return ToolResult(
                    success=True,
                    output=(
                        f"Diff: {delta}\n"
                        f"Days: {delta.days}\n"
                        f"Seconds: {delta.total_seconds()}\n"
                        f"Hours: {delta.total_seconds() / 3600}"
                    ),
                    metadata={
                        "days": delta.days,
                        "seconds": delta.total_seconds(),
                    },
                )
            
            else:
                return ToolResult(success=False, error=f"Unknown action: {action}", return_code=2)
        
        except Exception as e:
            return ToolResult(success=False, error=str(e), return_code=1)