using System.Globalization; using System.Text.Json; namespace AxCopilot.Services.Agent; /// 날짜·시간 변환, 타임존, 기간 계산 유틸리티 도구. public class DateTimeTool : IAgentTool { public string Name => "datetime_tool"; public string Description => "Date/time utility tool. Actions: " + "'now' — get current date/time in various formats; " + "'parse' — parse a date string into standard format; " + "'diff' — calculate difference between two dates; " + "'add' — add/subtract days/hours/minutes to a date; " + "'epoch' — convert between Unix epoch and datetime; " + "'format' — format a date into specified pattern."; public ToolParameterSchema Parameters => new() { Properties = new() { ["action"] = new() { Type = "string", Description = "Action to perform", Enum = ["now", "parse", "diff", "add", "epoch", "format"], }, ["date"] = new() { Type = "string", Description = "Date string (for parse/diff/add/format/epoch). For epoch: Unix timestamp in seconds.", }, ["date2"] = new() { Type = "string", Description = "Second date string (for diff action)", }, ["amount"] = new() { Type = "string", Description = "Amount to add (for add action). E.g. '7' for 7 days", }, ["unit"] = new() { Type = "string", Description = "Unit for add action", Enum = ["days", "hours", "minutes", "seconds", "months", "years"], }, ["pattern"] = new() { Type = "string", Description = "Format pattern (for format action). E.g. 'yyyy-MM-dd HH:mm:ss', 'ddd MMM d yyyy'", }, }, Required = ["action"], }; public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default) { var action = args.GetProperty("action").GetString() ?? ""; try { return Task.FromResult(action switch { "now" => Now(), "parse" => Parse(args), "diff" => Diff(args), "add" => Add(args), "epoch" => Epoch(args), "format" => FormatDate(args), _ => ToolResult.Fail($"Unknown action: {action}"), }); } catch (Exception ex) { return Task.FromResult(ToolResult.Fail($"DateTime 오류: {ex.Message}")); } } private static ToolResult Now() { var now = DateTime.Now; var utc = DateTime.UtcNow; var epoch = new DateTimeOffset(utc).ToUnixTimeSeconds(); return ToolResult.Ok( $"Local: {now:yyyy-MM-dd HH:mm:ss (ddd)} ({TimeZoneInfo.Local.DisplayName})\n" + $"UTC: {utc:yyyy-MM-dd HH:mm:ss}\n" + $"ISO: {now:O}\n" + $"Epoch: {epoch}\n" + $"Week: {CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)}"); } private static ToolResult Parse(JsonElement args) { var dateStr = args.TryGetProperty("date", out var d) ? d.GetString() ?? "" : ""; if (string.IsNullOrEmpty(dateStr)) return ToolResult.Fail("'date' parameter is required"); if (!DateTime.TryParse(dateStr, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) && !DateTime.TryParse(dateStr, CultureInfo.CurrentCulture, DateTimeStyles.None, out dt)) return ToolResult.Fail($"Cannot parse date: '{dateStr}'"); return ToolResult.Ok( $"Parsed: {dt:yyyy-MM-dd HH:mm:ss}\n" + $"Day: {dt:dddd}\n" + $"ISO: {dt:O}\n" + $"Epoch: {new DateTimeOffset(dt).ToUnixTimeSeconds()}"); } private static ToolResult Diff(JsonElement args) { var d1 = args.TryGetProperty("date", out var v1) ? v1.GetString() ?? "" : ""; var d2 = args.TryGetProperty("date2", out var v2) ? v2.GetString() ?? "" : ""; if (string.IsNullOrEmpty(d1) || string.IsNullOrEmpty(d2)) return ToolResult.Fail("'date' and 'date2' parameters are required"); if (!DateTime.TryParse(d1, out var dt1)) return ToolResult.Fail($"Cannot parse date: '{d1}'"); if (!DateTime.TryParse(d2, out var dt2)) return ToolResult.Fail($"Cannot parse date: '{d2}'"); var diff = dt2 - dt1; return ToolResult.Ok( $"From: {dt1:yyyy-MM-dd HH:mm:ss}\n" + $"To: {dt2:yyyy-MM-dd HH:mm:ss}\n\n" + $"Difference:\n" + $" {Math.Abs(diff.TotalDays):F1} days\n" + $" {Math.Abs(diff.TotalHours):F1} hours\n" + $" {Math.Abs(diff.TotalMinutes):F0} minutes\n" + $" {Math.Abs(diff.TotalSeconds):F0} seconds\n" + $" ({(diff.TotalDays >= 0 ? "forward" : "backward")})"); } private static ToolResult Add(JsonElement args) { var dateStr = args.TryGetProperty("date", out var dv) ? dv.GetString() ?? "" : ""; var amountStr = args.TryGetProperty("amount", out var av) ? av.GetString() ?? "0" : "0"; var unit = args.TryGetProperty("unit", out var uv) ? uv.GetString() ?? "days" : "days"; if (string.IsNullOrEmpty(dateStr)) return ToolResult.Fail("'date' parameter is required"); if (!DateTime.TryParse(dateStr, out var dt)) return ToolResult.Fail($"Cannot parse date: '{dateStr}'"); if (!double.TryParse(amountStr, out var amount)) return ToolResult.Fail($"Invalid amount: '{amountStr}'"); var result = unit switch { "days" => dt.AddDays(amount), "hours" => dt.AddHours(amount), "minutes" => dt.AddMinutes(amount), "seconds" => dt.AddSeconds(amount), "months" => dt.AddMonths((int)amount), "years" => dt.AddYears((int)amount), _ => dt.AddDays(amount), }; return ToolResult.Ok( $"Original: {dt:yyyy-MM-dd HH:mm:ss}\n" + $"Added: {amount} {unit}\n" + $"Result: {result:yyyy-MM-dd HH:mm:ss} ({result:dddd})"); } private static ToolResult Epoch(JsonElement args) { var input = args.TryGetProperty("date", out var dv) ? dv.GetString() ?? "" : ""; if (string.IsNullOrEmpty(input)) return ToolResult.Fail("'date' parameter is required"); // 숫자면 epoch → datetime if (long.TryParse(input, out var epoch)) { var dt = DateTimeOffset.FromUnixTimeSeconds(epoch); return ToolResult.Ok( $"Epoch: {epoch}\n" + $"UTC: {dt.UtcDateTime:yyyy-MM-dd HH:mm:ss}\n" + $"Local: {dt.LocalDateTime:yyyy-MM-dd HH:mm:ss}"); } // 문자열이면 datetime → epoch if (DateTime.TryParse(input, out var parsed)) { var e = new DateTimeOffset(parsed).ToUnixTimeSeconds(); return ToolResult.Ok( $"Date: {parsed:yyyy-MM-dd HH:mm:ss}\n" + $"Epoch: {e}"); } return ToolResult.Fail($"Cannot parse: '{input}'"); } private static ToolResult FormatDate(JsonElement args) { var dateStr = args.TryGetProperty("date", out var dv) ? dv.GetString() ?? "" : ""; var pattern = args.TryGetProperty("pattern", out var pv) ? pv.GetString() ?? "yyyy-MM-dd" : "yyyy-MM-dd"; if (string.IsNullOrEmpty(dateStr)) return ToolResult.Fail("'date' parameter is required"); if (!DateTime.TryParse(dateStr, out var dt)) return ToolResult.Fail($"Cannot parse date: '{dateStr}'"); return ToolResult.Ok(dt.ToString(pattern, CultureInfo.InvariantCulture)); } }