Initial commit to new repository
This commit is contained in:
197
src/AxCopilot/Services/Agent/DateTimeTool.cs
Normal file
197
src/AxCopilot/Services/Agent/DateTimeTool.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
/// <summary>날짜·시간 변환, 타임존, 기간 계산 유틸리티 도구.</summary>
|
||||
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<ToolResult> 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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user