Files
EugeneTes 5b780fac36 feat(telegram): per-chat time zones with /timezone, set_timezone and Telegram:DefaultTimeZone
ChatSettingsStore keeps each chat's IANA zone in <AgentDir>/telegram/chat-settings.json;
ChatTimeZoneService is the one path for a change (store, live session, scheduler re-evaluation,
reply text). ChatRegistry.TimeZoneFor now reads the store and new sessions start in the chat's zone.
/timezone, the set_timezone tool (ChatSettingsExtension), /status zone line, DEFAULT_TIMEZONE.

The store updates its cache under the write gate so the last file written is the newest state.
2026-09-27 10:29:29 +02:00

26 lines
1.3 KiB
C#

using System.Text.RegularExpressions;
using Volcanic.AgenticCore.Hosting;
namespace Volcanic.AgenticCore.Telegram.Chats;
/// <summary>Parses what a user (<c>/timezone</c>) or the agent (<c>set_timezone</c>) calls a zone. The stored form is the IANA id.</summary>
public static partial class ChatTimeZones
{
/// <summary>Accepts IANA ids and Windows ids (converted); rejects <c>Local</c> and bare offsets such as <c>+03:00</c> or <c>GMT+3</c>.</summary>
public static bool TryParse(string? input, out TimeZoneInfo zone, out string error)
{
var text = input?.Trim() ?? "";
zone = TimeZoneInfo.Utc;
error = $"Unknown time zone \"{text}\". Use an IANA name such as Europe/Berlin or America/New_York.";
if (text.Length == 0 || text.Equals("Local", StringComparison.OrdinalIgnoreCase) || BareOffset().IsMatch(text)) return false;
if (!TimeZoneInfo.TryFindSystemTimeZoneById(text, out var found)) return false;
if (TimeZoneIds.ToIana(found) is not { } iana || !TimeZoneInfo.TryFindSystemTimeZoneById(iana, out var resolved)) return false;
zone = resolved;
error = "";
return true;
}
[GeneratedRegex(@"^(utc|gmt)?\s*[+-]\s*\d", RegexOptions.IgnoreCase)]
private static partial Regex BareOffset();
}