Initial commit to new repository

This commit is contained in:
2026-04-03 18:22:19 +09:00
commit 4458bb0f52
7672 changed files with 452440 additions and 0 deletions

View 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));
}
}